package com.zmzncs.lmtc.module.park.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.zmzncs.lmtc.common.util.BolianUtil;
import com.zmzncs.lmtc.common.util.MpMessageUtil;
import com.zmzncs.lmtc.common.pojo.miwang.*;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OrderCardTypeEnum;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OrderStatusEnum;
import com.zmzncs.lmtc.common.pojo.enums.optionalvalue.order.OutTypeEnum;
import com.zmzncs.lmtc.common.pojo.mpMessage.InParkMessage;
import com.zmzncs.lmtc.common.pojo.mpMessage.OutParkMessage;
import com.zmzncs.lmtc.common.util.DateTimeUtil;
import com.zmzncs.lmtc.common.util.JsonUtil;
import com.zmzncs.lmtc.common.interactive.miwang.MiWangParkOpenapi;
import com.zmzncs.lmtc.common.util.SnowflakeIdUtil;
import com.zmzncs.lmtc.common.websocket.WebsocketUtil;
import com.zmzncs.lmtc.module.customer.dao.CarDao;
import com.zmzncs.lmtc.module.customer.dao.CardDao;
import com.zmzncs.lmtc.module.customer.dao.CustomerDao;
import com.zmzncs.lmtc.module.customer.pojo.car.CarDTO;
import com.zmzncs.lmtc.module.customer.pojo.card.HasCardDTO;
import com.zmzncs.lmtc.module.customer.pojo.card.CardHasForm;
import com.zmzncs.lmtc.module.customer.pojo.customer.CustomerDTO;
import com.zmzncs.lmtc.module.customer.service.CardService;
import com.zmzncs.lmtc.module.order.dao.OrderInfoDao;
import com.zmzncs.lmtc.module.order.pojo.order_info.OrderInfo;
import com.zmzncs.lmtc.module.order.pojo.order_info.OrderInfoDTO;
import com.zmzncs.lmtc.module.order.service.ParkReserveRecordService;
import com.zmzncs.lmtc.module.park.dao.ParkInfoDao;
import com.zmzncs.lmtc.module.park.pojo.parkinfo.*;
import com.zmzncs.lmtc.module.park.service.MiWangYunService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

import static com.zmzncs.lmtc.common.util.BolianUtil.parkUnionId;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author JD
 * @since 2020-09-29
 */
@Slf4j
@Service
public class MiWangYunServiceImpl implements MiWangYunService {

    @Autowired
    CardService cardService;
    @Autowired
    OrderInfoDao orderInfoDao;
    @Autowired
    CustomerDao customerDao;
    @Autowired
    ParkInfoDao parkInfoDao;
    @Autowired
    CardDao cardDao;
    @Autowired
    CarDao carDao;
    @Autowired
    MpMessageUtil mpMessageUtil;
    @Autowired
    ParkReserveRecordService parkReserveRecordService;
    @Autowired
    WebsocketUtil websocketUtil;

    @Value("${pay-test}")
    boolean payTest;

    @Override
    public MiWangRest uploadCarInData(MWParkCarInFrom form) {
        //  根据订单号查询订单
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(form.getUid());
        if (orderInfoDTO != null) {
            log.error("重复订单号【{}】", form.getUid());
            return new MiWangRest(500, "重复订单号");
        }
        //  根据车场编号获取停车场
        ParkInfoDTO parkInfoDTO = parkInfoDao.getMWParkByParkId(form.getParkingCode());
        //  添加进场订单
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderCode(form.getUid());   //订单编号
        orderInfo.setCarNumber(form.getPlateNo());   //车牌号码
        orderInfo.setParkId(Long.valueOf(form.getParkingCode()));
        orderInfo.setInTime(DateTimeUtil.strToTime(form.getArriveTime())); //进场时间
        orderInfo.setStatus(OrderStatusEnum.IN.getCode()); //订单状态（1：进场，2：出场）

        //1.无绑定车 或 无牌车只创建订单
        CarDTO carDTO = carDao.getCarByCarNumber(form.getPlateNo());
        if (carDTO == null || carDTO.getCustomerId() == null) {
            orderInfoDao.insert(orderInfo);
            return new MiWangRest(200, "无绑定车");
        }
        //  2.处理车牌没有正常出场的订单
        setNotOutOrder(form.getPlateNo());
        // 3. 创建订单
        orderInfo.setCustomerId(carDTO.getCustomerId()); //客户ID
        Integer rsultCode = orderInfoDao.insert(orderInfo);
        try {
            //  客户详情
//            CustomerDTO customerDTO = customerDao.getCustomerById(carDTO.getCustomerId());
            if (rsultCode > 0) {
                InParkMessage message = new InParkMessage();
//                message.setUnionid(customerDTO.getUnionid());
                message.setCarNumber(carDTO.getCarNumber());
                message.setParkName(parkInfoDTO != null ? parkInfoDTO.getParkName() : "");
                message.setInTime(form.getArriveTime());
                //4. 微信公众号发送进场信息
                //mpMessage.inParkMessage(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信公众号发送进场信息error【{}】", e.getStackTrace());
        }

        //上传进场记录到泊链
        if (rsultCode > 0 && parkInfoDTO != null){
            long inTime = orderInfo.getInTime().getTime() / 1000;//入场时间（时间戳，秒）
            Boolean addInParkOrder = BolianUtil.addInParkOrder(form.getParkingCode(), form.getPlateNo(), inTime, form.getUid(), parkInfoDTO.getEmptyParkSpace());
            if (addInParkOrder){
                log.info("车场入场记录上传泊链成功");
            }
        }

        //更新预约记录状态
        parkReserveRecordService.updateStatusByCarNumber(form.getPlateNo(),orderInfo.getParkId(),
                orderInfo.getInTime(),orderInfo.getId());

        return new MiWangRest(200, "成功");
    }

    @Override
    public MiWangRest uploadCarOutData(MWParkCarOutFrom form) {
        //1. 订单 是否支付成功
        //  根据订单号查询订单
        OrderInfoDTO orderInfoDTO = orderInfoDao.getByOrderCode(form.getUid());
        if (orderInfoDTO != null && (orderInfoDTO.getStatus().intValue() == 3 || orderInfoDTO.getStatus().intValue() == 4)) {
            log.error("停车场【{}】 ， 订单号 {} , 已出场！", form.getParkingCode(), form.getUid());
            return new MiWangRest(500, "订单号已完成已离场");
        } else if (orderInfoDTO == null) {
            log.error("无此订单，停车场【{}】 ， 订单号 {}", form.getParkingCode(), form.getUid());
            return new MiWangRest(500, "无此订单");
        }

        //  根据泊链停车场编号获取停车场
        ParkInfoDTO parkInfoDTO = parkInfoDao.getMWParkByParkId(form.getParkingCode());
        if (parkInfoDTO == null) {
            log.error("停车场【{}】不存在", form.getParkingCode());
        }

        //  入场时间
        Date inTime = DateTimeUtil.strToTime(form.getArriveTime());
        //  入场时间
        Date outTime = DateTimeUtil.strToTime(form.getEndTime());

        //  是否有会员卡，如果有月卡则直接出去，如果没有月卡有时长卡则扣时长
        OrderCardTypeEnum orderCardTypeEnum = OrderCardTypeEnum.NO;

        //  向移动端发送进场消息
        CarDTO carDTO = carDao.getCarByCarNumber(form.getPlateNo());
        if (carDTO == null || carDTO.getCustomerId() == null) {
            //  1. 修改订单状态
            updateOrderStatus(orderInfoDTO.getId(), inTime.getTime(), outTime.getTime(), null ,
                    new BigDecimal(form.getDealFee()), orderCardTypeEnum);
            return new MiWangRest(200, "无绑定车");
        }
        Long customerId = carDTO.getCustomerId();
        //  如果数据库没有该停车场，则无法计算会员卡
        if (parkInfoDTO != null) {
            CardHasForm cardHasForm = new CardHasForm();
            cardHasForm.setOrderCode(form.getUid());   //  订单号
            cardHasForm.setParkId(parkInfoDTO.getId());   //  停车场id
            cardHasForm.setCarNumber(form.getPlateNo());     //  车牌号
            cardHasForm.setBeginTime(inTime);    //  开始时间
            cardHasForm.setEndTime(outTime);
            // 2. 是否有会员卡，扣除时长 。 如果有月卡则直接出去，如果没有月卡有时长卡则扣时长
            orderCardTypeEnum = cardService.hasCard(cardHasForm).getOrderCardTypeEnum();
        }
        CustomerDTO customerDTO = customerDao.getCustomerById(carDTO.getCustomerId());
        //  3. 修改订单状态
        if (orderInfoDTO != null) {
            //  修改订单状态
            updateOrderStatus(orderInfoDTO.getId(), inTime.getTime(), outTime.getTime(), customerId,
                    new BigDecimal(form.getDealFee()), orderCardTypeEnum);
        }

        // 4. 处理车牌没有正常出场的订单
        setNotOutOrder(form.getPlateNo());
        //  5. 向移动端发送进场消息
        try {
            //  向移动端发送进场消息
            websocketUtil.sendMessage(customerId.toString(), "car-out-park");

            // 微信公众号发送离场通知
            if (customerDTO != null && customerDTO.getUnionid() != null) {
                OutParkMessage message = new OutParkMessage();
                message.setUnionid(customerDTO.getUnionid());
                message.setCarNumber(form.getPlateNo());
                message.setParkName(parkInfoDTO.getParkName());
                message.setInTime(form.getArriveTime());
                message.setOutTime(form.getEndTime());
                message.setPaidAmount(form.getDealFee());
                message.setParkDuration(DateTimeUtil.durationTime(outTime.getTime() - inTime.getTime()));

                int code = orderCardTypeEnum.getCode();
                String carNum = form.getPlateNo();
                Long parkId = orderInfoDTO.getParkId();

                //公众号离场通知（会员卡信息）
              //  mpMessage.monthAndDurationMessage(message,code,carNum,customerId,parkId);
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信公众号发送离场信息error", e.getStackTrace());
        }

        //车场出场记录上传泊链
        if (parkInfoDTO != null) {
            Double total = Double.parseDouble(form.getDealFee()) / 100; //实际支付金额
            String resultStr = BolianUtil.addOutParkOrder(form.getParkingCode(), form.getPlateNo(), inTime.getTime() / 1000,
                    outTime.getTime() / 1000, form.getUid(), parkInfoDTO.getEmptyParkSpace(), total.toString());
            if(StringUtils.isNotBlank(resultStr)){
                JsonNode jsonNode = JsonUtil.getJsonNode(resultStr);
                String state = jsonNode.get("state").toString();
                if(state.equals("1")){
                    log.info("车场出场记录上传泊链成功");
                }
            }
        }

        return new MiWangRest(200, "成功");
    }

    @Override
    public MiWangRest pushReleaseModeForbidden(MWPushReleaseModeForbiddenFrom form) {
        CarDTO carDTO = carDao.getCarByCarNumber(form.getPlate());
        if (carDTO == null || carDTO.getCustomerId() == null) {
            log.info("临时车【{}】，禁止放行", form.getPlate());
            return new MiWangRest(200, "无绑定车");
        }
        ParkInfoDTO parkInfoDTO = parkInfoDao.getMWParkByParkId(form.getParkingCode());
        if (parkInfoDTO == null) {
            log.error("停车场【{}】不存在", form.getParkingCode());
            return new MiWangRest(200, "没有此停车场，失败");
        }

        //  入场时间
        Date inTime = null ;
        //  入场时间
        Date outTime = DateTimeUtil.strToTime(form.getPassTime());
        //  根据订单号查询订单
        List<OrderInfoDTO> OrderInfoDTOs = orderInfoDao.getNotOutOrderByCarNumber(form.getPlate());
        if(OrderInfoDTOs!=null && OrderInfoDTOs.size()>0){
            inTime = OrderInfoDTOs.get(0).getInTime();
        }
        if (inTime == null) {
            log.error("车牌号【{}】没有在场记录。", form.getPlate() );
            return new MiWangRest(200, "车牌号"+form.getPlate() +"没有在场记录。");
        }
        Long customerId = carDTO.getCustomerId();

        CardHasForm cardHasForm = new CardHasForm();
        cardHasForm.setParkId(Long.valueOf(form.getParkingCode()));
        cardHasForm.setCarNumber(form.getPlate());
        cardHasForm.setBeginTime(inTime);
        cardHasForm.setEndTime(outTime);
        HasCardDTO hasCardDTO = cardService.hasCard(cardHasForm);
        // 判断是否会员 ， 只处理会员
        if (hasCardDTO.getOrderCardTypeEnum().equals(OrderCardTypeEnum.NO)) {
            log.info("车牌{}，不是会员，无需处理。", form.getPlate());
        }
        //1.计费查询getCurrentChargeBill
        String dataCurrentChargeBillStr = MiWangParkOpenapi.mwpark_sandbox_getCurrentChargeBillHttpSync(parkInfoDTO.getMiwangPartnerCode(), form.getPlate());
        JsonNode node = JsonUtil.getJsonNode(dataCurrentChargeBillStr);
        log.info("费用信息：{}", node);

        String code = JsonUtil.getJsonValue(node, "code");
        if (StringUtils.isBlank(code) ||  !code.equals("200")) {
           return new MiWangRest(200, "实时计价查询失败");
        }
        String data = JsonUtil.getJsonValue(node, "data");
        MWCurrentChargeBillRes mwCurrentChargeBillRes = JsonUtil.JSONToObj(data, MWCurrentChargeBillRes.class);
        log.info("订单费用：{}", mwCurrentChargeBillRes);
        //  应付金额（元）
        String cost = payTest == true ? "0.01" : mwCurrentChargeBillRes.getCost();
        //  预支付
        BolianUtil.prePay(OrderInfoDTOs.get(0).getOrderCode(), mwCurrentChargeBillRes.getBillCode(), form.getPlate(), parkUnionId, form.getParkingCode(), cost, customerId);
//        BolianUtil.prePay("A1_2C16074026496157", mwCurrentChargeBillRes.getBillCode(), "京A88888", "200159", "laoyao", cost, 1308348242228080641L, null);

        //不扣 时长或月租， 离场处理
        return new MiWangRest(200, "成功");
    }

    @Override
    public Map<String, Object> getParkingLotByCode(String data) {
        return null;
    }

    @Override
    public MiWangRest uploadParkingInfo(MWUploadParkingInfoForm form) {

        ParkInfoDTO parkInfoDTO = new ParkInfoDTO();
        parkInfoDTO.setParkName(form.getParkingName());
        parkInfoDTO.setParkAddress(form.getAddress());
        parkInfoDTO.setParkSource(2);
        parkInfoDTO.setPriceDesc(form.getFeeScale());
        parkInfoDTO.setTotalParkSpace(form.getTotalBerthNum());
        parkInfoDTO.setLongitude(form.getGmapX());
        parkInfoDTO.setLatitude(form.getGmapY());
        parkInfoDTO.setUpdateFromBolian(1);
        parkInfoDTO.setIsShow(1);
        parkInfoDTO.setBolianParkUnionId(form.getParkingCode());
        parkInfoDTO.setBolianComId(form.getParkingCode());
        parkInfoDTO.setBolianParkId(form.getParkingCode());

        String parkingCode = form.getParkingCode();
        LambdaQueryWrapper<ParkInfo> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(ParkInfo::getBolianParkId,parkingCode)
                .eq(ParkInfo::getParkSource,2);
        ParkInfo parkInfo = parkInfoDao.selectOne(queryWrapper);

        if (parkInfo != null){
            parkInfoDTO.setId(parkInfo.getId());
            //更新停车场信息
            parkInfoDao.updatePark(parkInfoDTO);
        } else {
            Long parkId = new SnowflakeIdUtil(new Random().nextInt(9), new Random().nextInt(9)).nextId();
            parkInfoDTO.setId(parkId);
            //新增停车场信息
            parkInfoDao.insertPark(parkInfoDTO);
        }

        return new MiWangRest(200, "成功");
    }

    @Override
    public MiWangRest uploadParkingState(MWUploadParkingStateForm form) {
        ParkInfoDTO parkInfoDTO = new ParkInfoDTO();
        parkInfoDTO.setTotalParkSpace(form.getAllNum());
        parkInfoDTO.setEmptyParkSpace(form.getLeavNum());
        parkInfoDTO.setUpdateTime(new Date());

        String parkingCode = form.getParkingCode();
        LambdaQueryWrapper<ParkInfo> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(ParkInfo::getBolianParkId,parkingCode)
                .eq(ParkInfo::getParkSource,2);
        ParkInfo parkInfo = parkInfoDao.selectOne(queryWrapper);
        if (parkInfo != null){
            parkInfoDTO.setId(parkInfo.getId());
            //更新停车场信息
            parkInfoDao.updatePark(parkInfoDTO);
        }

        return new MiWangRest(200, "成功");
    }


    /**
     * 处理车牌没有正常出场的订单
     */
    public void setNotOutOrder(String carNumber) {
        //  根据车牌号查询未出场订单
        List<OrderInfoDTO> orderInfoDTOList = orderInfoDao.getNotOutOrderByCarNumber(carNumber);

        //  设置异常出场时间
        for (OrderInfoDTO orderInfoDTO : orderInfoDTOList) {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setId(orderInfoDTO.getId());  //  订单id
            orderInfo.setOutTime(new Date());   //  出场时间
            orderInfo.setOutMsg("没有正常出场记录，暂时将出场时间记录为当前时间" + DateTimeUtil.getTime());
            orderInfo.setOutType(OutTypeEnum.EXCEPTION.getCode());  //  出场类型（1：正常出场，2：异常出场）
            orderInfoDao.updateById(orderInfo);

            log.info("处理车牌没有正常出场的订单：{}", orderInfoDTO.getOrderCode());
        }
    }


    /**
     * 修改订单状态
     *
     */
    void updateOrderStatus(Long orderId, Long inTime, Long outTime, Long customerId, BigDecimal bolianTotalMoney,
                           OrderCardTypeEnum orderCardTypeEnum) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);  //  订单id
        orderInfo.setOutTime(new Date(outTime));   //  出场时间
        orderInfo.setCustomerId(customerId);   //  客户id
        orderInfo.setStatus(OrderStatusEnum.OUT.getCode()); //  订单状态（1：进场，2：出场）
        //  是否有交易记录
        if (bolianTotalMoney.compareTo(BigDecimal.ZERO) > 0) {
            orderInfo.setOutType(OutTypeEnum.NORMAL.getCode());  //  出场类型（1：正常出场，2：异常出场）
            orderInfo.setOutMsg("正常");
        } else {
            orderInfo.setOutType(OutTypeEnum.EXCEPTION.getCode());  //  出场类型（1：正常出场，2：异常出场）
            orderInfo.setOutMsg("没有支付记录");
        }

        orderInfo.setCardType(orderCardTypeEnum.getCode());  //  会员卡类型（1：月租卡，2：时长卡，3：其他）
        //  时间戳（转分钟）
        long parkDuration = outTime / 1000 / 60 - inTime / 1000 / 60;
        orderInfo.setParkDuration(parkDuration);

        orderInfoDao.updateById(orderInfo);
    }
}
